package m.html;

import m.common.action.IVueEcharts;
import m.common.action.IVueExcel;
import m.common.action.VueResult;
import m.common.model.Model;
import m.common.model.util.*;
import m.html.element.Button;
import m.html.element.Element;
import m.html.element.Icon;
import m.html.element.Text;
import m.html.element.attr.ButtonType;
import m.html.element.attr.Size;
import m.html.list.*;
import m.html.util.EchartsUtil;
import m.html.util.ExcelUtil;
import m.html.util.VueListUtil;
import m.system.db.DBManager;
import m.system.db.DataRow;
import m.system.lang.HtmlBodyContent;
import m.system.util.ArrayUtil;
import m.system.util.JSONMessage;
import m.system.util.ObjectUtil;
import m.system.util.StringUtil;
import manage.util.excel.ExcelObject;
import manage.util.excel.SheetObject;
import manage.util.excel.SheetRow;

import java.util.*;

public class VueListResult extends VueResult implements IVueExcel, IVueEcharts {
	private final String title;
	private String subTitle;
	private String describe;
	private String selectField;
	private int height;
	private boolean isRadio=false;
	private String selectChecked="1==2";
	private final Class<?> clazz;
	private final String dataUrl;
	private final String actionName;
	private final String queryData;
	private final List<ListColumn> cols;

	private QueryPage page;
	private QueryOrder order;
	private final List<QueryCondition> conditions;
	private final List<QueryOrder> orders;
	
	private final Set<String> queryFields;//所有元素所需的查询列
	private final List<ListSearch> searchMap;
	private Map<String,String> params;
	private final List<IListElement> buttons;
	
	private Integer scol=0;			//单元格合并开始列
	private Integer slength=0;		//单元格合并长度.

	private boolean fnExcel=false;
	private boolean fnChart=false;
	/**
	 * 构造方法
	 *  clazz 查询的类
	 *  dataUrl 查询数据的地址
	 *  queryData list代表查询数据,count代表记录数和汇总数据  空或其他返回页面
	 */
	public <T extends Model> VueListResult(String title,Class<T> clazz,String dataUrl,String queryData,Map<String,String> params) {
		this.title=title;
		this.subTitle="";
		this.describe="";
		this.selectField="";
		this.clazz=clazz;
		this.dataUrl=dataUrl;
		this.queryData=queryData;
		this.cols= new ArrayList<>();
		this.queryFields= new HashSet<>();
		this.searchMap= new ArrayList<>();
		this.params=params;
		this.page=new QueryPage();
		if(null==this.params) this.params= new HashMap<>();
		this.buttons= new ArrayList<>();
		this.conditions= new ArrayList<>();
		this.orders= new ArrayList<>();
		this.height=0;
		String[] urlParts=dataUrl.split("/");
		String[] actions=new String[urlParts.length-2];
		System.arraycopy(urlParts,1,actions,0,actions.length);
		this.actionName=ArrayUtil.connection(actions, "/");
	}
	/**
	 * 添加列
	 *  col
	 * 
	 */
	public VueListResult addCol(ListColumn col) {
		String view=this.params.get("view_"+col.getField());
		if(!StringUtil.isSpace(this.queryData)) {
			col.setShow(StringUtil.noSpace(view).equals("true"));
		}
		this.cols.add(col);
		if(col.getShow()) {
			this.queryFields.addAll(col.getParamFields());
		}
		if(col.getFilter()) {
			if(col.getDisItems().size()>0) {
				col.setFilter(SearchType.HIDDEN_EQ);
			}
			this.addSearch(
				ListSearch.field(col.getTitle(),col.getField(),col.getFilterType(),col.getDisItems())
					.setDictType(col.getDictType()).setDictTree(col.isDictTree())
			);
		}
		return this;
	}
	/**
	 * 添加表格按钮
	 * 
	 */
	public VueListResult addButton(IListElement... buttons) {
		for(IListElement button : buttons) {
			this.buttons.add(button);
			if(button instanceof ListButton) {
				((ListButton)button).setSize(Size.DEFAULT);
			}
			if(button instanceof ListDropdown) {
				((ListDropdown)button).setSize(Size.DEFAULT);
			}
			this.queryFields.addAll(button.getParamFields());
		}
		return this;
	}
	/**
	 * 添加查询字段, 可用于自定义属性使用
	 *  queryFields
	 * 
	 */
	public VueListResult addQueryFields(String... queryFields) {
		this.queryFields.addAll(Arrays.asList(queryFields));
		return this;
	}
	/**
	 * 添加查询元素
	 * 
	 */
	public VueListResult addSearch(ListSearch searchItem) {
		this.searchMap.add(searchItem);
		return this;
	}
	/**
	 * 设置单选框或多选框
	 *  field
	 *  isRadio 是否单选
	 *  checkedExp 选择js表达式  record记录行
	 * 
	 */
	public VueListResult setSelection(String field,boolean isRadio,String checkedExp) {
		this.selectField=field;
		this.isRadio=isRadio;
		if(!StringUtil.isSpace(checkedExp)) this.selectChecked=checkedExp;
		return this;
	}
	/**
	 * 设置单选框或多选框
	 *  field
	 *  isRadio
	 * 
	 */
	public VueListResult setSelection(String field,boolean isRadio) {
		return setSelection(field, isRadio, null);
	}
	public VueListResult setPage(QueryPage page) {
		this.page = page;
		return this;
	}
	public VueListResult setOrder(QueryOrder order) {
		this.order = order;
		return this;
	}
	public VueListResult setSubTitle(String subTitle) {
		this.subTitle = subTitle;
		return this;
	}
	public VueListResult setDescribe(String describe) {
		this.describe=describe;
		return this;
	}
	/**
	 * 添加查询数据时的默认查询条件
	 *  cond
	 * 
	 */
	public VueListResult addCondition(QueryCondition... cond) {
		this.conditions.addAll(Arrays.asList(cond));
		return this;
	}
	public VueListResult addOrder(QueryOrder... order) {
		this.orders.addAll(Arrays.asList(order));
		return this;
	}
	public VueListResult setMergeCellsInfo(Integer scol,Integer slength){
		this.scol=scol;
		this.slength=slength;
		return this;
	}
	public VueListResult setFunction(boolean fnExcel,boolean fnChart){
		this.fnExcel=fnExcel;
		this.fnChart=fnChart;
		return this;
	}
	public void setHeight(int height) {
		this.height = height;
	}

	/**
	 * list：列表   count：行数，合计
	 */
	private JSONMessage getQueryResult(String type) throws Exception {
		JSONMessage result=new JSONMessage();
		result.push("code", 0);
		result.push("asMap", new HashMap<String,String>());
		if(type.contains("list")) {
			int sc=this.scol;
			int sl=this.slength;
			StringBuilder colSb = new StringBuilder();
			for (int i = 0; i < this.cols.size(); i++) {
				ListColumn col = this.cols.get(i);
				if (!col.getShow()){
					if(this.scol>i) sc--;
					else if(this.scol+this.slength>i) sl--;
					continue;
				}
				if (null != col.getGroupTitle() && col.getGroupTitle().length > 0) {
					String json = toTitleJson(col, i == this.cols.size() - 1);
					if (!StringUtil.isSpace(json)) {
						colSb.append(json);
						colSb.append(",");
					}
				} else {
					String json = toTitleJson();
					if (!StringUtil.isSpace(json)) {
						colSb.append(json);
						colSb.append(",");
					}
					colSb.append(col.toJsonString());
					colSb.append(",");
				}
			}
			String last = toLastJson();
			if (null != last) colSb.append(last).append(",");
			result.push("columns", new HtmlBodyContent("[" + colSb.deleteCharAt(colSb.length() - 1) + "]"));
			result.push("scol",sc);
			result.push("slength",sl);
		}
		for(ListSearch ls : this.searchMap) {
			if(ls.isField()) {
				this.conditions.addAll(ls.toCondition(this.params));
			}
		}
		if(StringUtil.isSpace(this.selectField)) this.queryFields.add(this.selectField);
		Map<String,String> expMap= new HashMap<>();
		Map<String,ListCount> sumMap= new LinkedHashMap<>();
		for(ListColumn col : this.cols) {
			if(!col.getShow()) continue;
			String exp=col.getFieldExpression();
			if(!StringUtil.isSpace(exp)) expMap.put(col.getField(), exp);
			if(col.getCount()!=ListCount.NONE){
				String k=col.getField();
				sumMap.put(k,col.getCount());
			}
		}
		this.orders.add(0, this.order);
		if(type.contains("list")){
			result.push("data",
				ModelQueryList.getModelList((Class<Model>)this.clazz,
					this.queryFields.toArray(new String[] {}),
					this.page,
					QueryCondition.and(this.conditions.toArray(new QueryCondition[] {})),
					expMap,
					this.orders.toArray(new QueryOrder[] {})
				)
			);
		}
		if(type.contains("count")){
			ModelQueryList mq=ModelQueryList.instance((Class<Model>)this.clazz,
				this.queryFields.toArray(new String[] {}),
				this.page,
				QueryCondition.and(this.conditions.toArray(new QueryCondition[] {})),
				expMap,
				this.orders.toArray(new QueryOrder[] {})
			);
			DataRow ds=DBManager.executeQuery(mq.getCountParameter()).rows().get(0);
			Integer count=0;
			if(null!=ds){
				count=ds.get(Long.class, "num").intValue();
			}
			result.push("count", count);
			//填充合计行
			if(sumMap.size()>0){
				ModelQueryList query=ModelQueryList.instance((Class<Model>)this.clazz,
						this.queryFields.toArray(new String[] {}),
						null,
						QueryCondition.and(this.conditions.toArray(new QueryCondition[] {}))
				);
				QueryParameter qp=query.getQueryParameter();
				StringBuilder sql=new StringBuilder("SELECT '' nofield");
				for(String k : sumMap.keySet()){
					sql.append(",").append(sumMap.get(k));
					if(sumMap.get(k)==ListCount.SUM){
						sql.append("(ifnull(a.").append(query.getAlias4Field(k)).append(",0)) ");
					}else{
						sql.append("(a.").append(query.getAlias4Field(k)).append(") ");
					}
					sql.append(k.replaceAll("\\.","_"));
				}
				sql.append(" FROM (").append(qp.getSql()).append(") a");
				DataRow dr=DBManager.queryFirstRow(sql.toString(),qp.getValueList().toArray(new Object[]{}));
				if(null!=dr){
					List<Object[]> ls= new ArrayList<>();
					if(!StringUtil.isSpace(this.selectField)) {
						ls.add(new Object[] {"","","","left",true});
					}
					for(ListColumn col : this.cols) {
						if(!col.getShow()) continue;
						Object n=dr.get(col.getField().replaceAll("\\.","_"));
						if(null!=n) {
							ls.add(new Object[] {n,col.getNumberFormat()+col.getDateFormat(),col.getAlign().toString(),col.getFixed().toString(),col.getType(),col.isFalse()});
						}else {
							ls.add(new Object[] {"","","",col.getFixed().toString(), col.getType(),col.isFalse()});
						}
					}
					result.push("summary", ls);
				}
			}else {
				result.push("summary", new ArrayList<>());
			}
		}
		return result;
	}
	@Override
	public String outHtml() {
		if(!StringUtil.isSpace(this.queryData)) {
			try {
				return ObjectUtil.toString(getQueryResult(this.queryData));
			} catch (Exception e) {
				e.printStackTrace();
				return "{code:1,msg:\""+e.getMessage()+"\"}";
			}
		}
		Element searchPanel=VueListUtil.toSearchPanel(this.searchMap);
		String order="{}";
		StringBuilder colTemplate=new StringBuilder();
		for(ListColumn col : this.cols) {
			if(ListSortOrder.NONE!=col.getSortOrder()) {
				order="{name:\""+col.getField()+"\",oper:\""+col.getSortOrder().getOrder()+"\"}";
			}
			colTemplate.append(col.toExtendString(this.actionName));
		}
		Element div=Element.tag("div")
		.E(
			Element.tag("a-page-header")
			.A(":back-icon","false").A("@back","back(false)").A("title",this.title)
			.E(
				Element.tag("template").A("#subTitle").E(new Text(this.subTitle))
			)
			.E(
				Element.tag("template").A("#extra")
				.E(null!=searchPanel?new Button().E(new Text("筛选")).setExtend(" @click=\"showSearch=true;\" "):null)
				.E(this.buttons)
				.E(
					Element.tag("m-back").E(new Text("{{param.openType=='PAGE'?'返回':'关闭'}}")).setExtend(" v-if=\"param.isOpen\" @click=\"back(false);\" ")
				)
			)
		)
		.E(
			Element.tag("div").A("style", "padding:0 15px;").E(new Text(this.describe))
		).E(searchPanel)
		.E(
			Element.tag("a-card").A("hoverable").A(":bodyStyle", "{padding:0}").A("style","margin:0 15px;")
			.E(
				Element.tag("a-table").A(":custom-row","customRow").A("ref", "vueListResultTable").A(":sticky","{getContainer:stickyGetContainer}")
				.A(":row-selection",StringUtil.isSpace(this.selectField)?"null":"{fixed:true,type:'"+(this.isRadio?"radio":"checkbox")+"',selectedRowKeys:selectedRowKeys,onChange:selectChange}")
				.A(":row-key","selectField").A("style", "margin:0 0 -1px;").A(":scroll", "{x:width,y:height}")
				.A("size","small").A(":columns","columns").A(":data-source","tableData").A("@change","tableChange")
				.A(":pagination","false").A("tableLayout","fixed").A("@resizeColumn","handleResizeColumn")
				.E(new Text(colTemplate.toString()))
				.E(Element.tag("template").A("#emptyText").E(new Text("空")))
				.E(
					Element.tag("template").A("#filter-icon","filtered")
					.E(
						Element.tag("div").A("style","display:flex;align-items:center;justify-content:center;")
						.E(new Icon("icon-icon-32").setSize(17).setColor("#1890ff").A("v-if","isHasSearch(filtered.column.key)"))
						.E(new Icon("icon-icon-33").setSize(17).A("v-else"))
					)
				)
				.E(
					Element.tag("template").A("#summary")
					.E(
						Element.tag("a-table-summary").A("fixed").A("v-if", "tableSummary.length>0")
						.E(
							Element.tag("a-table-summary-row")
							.E(
								Element.tag("a-table-summary-cell").A("v-for", "(item,index) in tableSummary").A(":index","index")
								.E(
									Element.tag("div").A("class","td-wrap").A("style","-webkit-line-clamp:1;").A(":style", "{textAlign:item[2],color:(paginationLoad?'#9999':'')}")
									.A(":title","formatter('',item[0],item[1])").E(new Text("{{formatter('',item[0],item[1])}}"))
								)
							)
						)
					)
				)
			)
		)
		.E(
			Element.tag("div").A("style","padding:10px 15px;")
			.E(
				Element.tag("a-popover").E(
					Element.tag("template").A("#content").E(new Text("上次查询耗时{{queryLong}}秒<div v-if=\"countLong\">上次汇总耗时{{countLong}}秒</div>")),
					new Button(ButtonType.PRIMARY).E(new Icon("icon-icon-72")).setExtend(" @click=\"queryData();\" style=\"float:left;margin:0 10px 6px 0;\" ")
				),
				Element.tag("a-popover").E(
					Element.tag("template"),
					Element.tag("a-popover").A("v-model:visible","showView").A("@visibleChange","hideResetInputParams")
					.E(
						new Button().E(new Icon("icon-column")).setExtend(" @click=\"showView=true;\" style=\"float:left;margin:0 10px 6px 0;\" "),
						Element.tag("template").A("#content").E(VueListUtil.toViewColumnElement(this.cols,this.params))
					)
				),
				this.fnExcel?Element.tag("a-popover").E(
					Element.tag("template").A("#content").E(new Text("<div v-if=\"downExcel\">正在导出Excel</div><div v-else>点击导出Excel</div>")),
					new Button().A(":disabled","downExcel").E(new Icon("icon-icon-126")).setExtend(" @click=\"excelData();\" style=\"float:left;margin:0 10px 6px 0;\" ")
				):null,
				this.fnChart?Element.tag("a-popover").E(
					Element.tag("template").A("#content").E(EchartsUtil.toPopoverContent(this.cols)),
					new Button().E(new Icon("icon-icon-134")).setExtend(" style=\"float:left;margin:0 10px 6px 0;\" ")
				):null
			)
			.E(Element.tag("a-typography-text").A("type","danger").A("v-if","paginationLoad").A("style","float:left;margin-right:10px;line-height:32px;").E(new Text("汇总中")))
			.E(null==page?Element.tag("div").A("style","line-height:32px;").E(new Text("共{{tableCount}}条")):
				Element.tag("a-pagination").A("v-model:current","page.index").A("v-model:page-size","page.num")
				.A(":total","tableCount").A("show-less-items").A("@change","queryData").A("show-size-changer")
				.A("@show-size-change","queryData").A(":disabled","paginationLoad")
				.A(":show-total","(total, range) => '共'+total+'条'")
				.A(":page-size-options","["+(null==page?"'9999999999'":"'10','20','50','100','200','500'")+"]")
				.E(new Text("<template #buildOptionText=\"props\"><span v-if=\"props.value !== '9999999999'\">{{ props.value }}条/页</span><span v-else>-全部-</span></template>"))
			)
		);
		
		StringBuilder sb=new StringBuilder();
		sb.append("{")
		.append(" template:`").append(div).append("`,")
		.append(" props: ['localeZh','param','parentVue'],")
		.append(" data(){")
		.append("  return{")
		.append("   showSearch:false,showView:false,queryLong:'',countLong:'',downExcel:false,");
		String ps= "{}";
		try {
			if(null!=this.params) ps=ObjectUtil.toString(this.params);
		}catch(Exception ignored) {}
		sb.append("   searchParams:{},");
		sb.append("   inputParams:Object.assign(").append(ps).append(",{");
		for(ListSearch ls : this.searchMap)  sb.append(ls.outField());
		sb.append("}),")
		.append("   dataUrl:\"").append(this.dataUrl).append("\",")
		.append("	columns:[],")
		.append("   colWidth:{},")
		.append("   asMap:{},")
		.append("	paginationLoad:false,")
		.append("	pagination:{},") 
		.append("   tableData:[],")
		.append("	tableSummary:[],")
		.append("   tableSubtotal:[],")
		.append("   tableSubfield:'',")
		.append("   tableCount:0,")
		.append("	width:0,")
		.append("	height:").append(this.height<=0?"null":this.height).append(",")
		.append("   page:{index:").append(null!=page?page.getIndex():"1").append(",num:").append(null!=page?page.getNum():"100000").append("},")
		.append("   order:").append(order).append(",")
		.append("   scol:").append(this.scol).append(",")
		.append("   slength:").append(this.slength).append(",")
		.append("   selectField:'").append(this.selectField).append("',")
		.append("   selectedRowKeys:[]");
		sb.append("};")//return {};
		.append(" },")
		.append(" mounted(){");
		if(null!=this.params) {
			for(ListSearch ls : this.searchMap) {
				if(ls.getDisItems().size()>0||!StringUtil.isSpace(ls.getDictType())) {
					String str=StringUtil.noSpace(this.params.get(ls.getField()));
					if(str.matches("^\\[.*\\]$")) {}
					else if(!StringUtil.isSpace(str)) str=ArrayUtil.toString(str.split(","));
					else str="[]";
					sb.append(" this.inputParams['").append(ls.getField()).append("']=").append(str).append(";");
				}else if(ls.getType().toString().contains("RANGE")) {
					sb.append(" this.inputParams['").append(ls.getField()).append("_down']=\"").append(StringUtil.noSpace(this.params.get(ls.getField()+"_down"))).append("\";");
					sb.append(" this.inputParams['").append(ls.getField()).append("_up']=\"").append(StringUtil.noSpace(this.params.get(ls.getField()+"_up"))).append("\";");
				}else {
					sb.append(" this.inputParams['").append(ls.getField()).append("']=\"").append(StringUtil.noSpace(this.params.get(ls.getField()))).append("\";");
				}
			}
		}
		sb.append("  this.queryData(1);")
		.append(" },")
		.append(" methods:Object.assign(mo.listResult.methods,{")
			.append("selectChange:function(srk, sr){this.selectedRowKeys=srk;this.notify(true,'listResult,queryData',{selectedRowKeys:this.selectedRowKeys,tableData:this.tableData});},")
			.append("customRow:function(record,index){")
				.append("return {")
					.append("onDblclick:(event)=>{this.setSelected(index,record,true);}")
				.append("};")
			.append("},")
			.append("setSelected:function(index,record,flag){")
				.append("if(").append(this.selectChecked).append("||flag){")
				.append(" if(this.selectedRowKeys.indexOf(record[this.selectField])>-1)")
				.append("  this.selectedRowKeys.remove(record[this.selectField]);")
				.append(" else")
				.append("  this.selectedRowKeys.push(record[this.selectField]);")
				.append("}")
			.append("}")
		.append(" })")
		.append("}");
		return sb.toString();
	}
	private JSONMessage jtemp=null;
	private String toTitleJson() {
		if(null!=jtemp) {
			String str=jtemp.toJSONString();
			jtemp=null;
			return str;
		}
		return null;
	}
	private String toTitleJson(ListColumn col,boolean isLast) {
		String[] strs=col.getGroupTitle();
		if(null==jtemp) {
			jtemp=new JSONMessage();
			jtemp.push("title", strs[0]);
			jtemp.push("children", new ArrayList<>());
		}
		if(jtemp.get("title").equals(strs[0])) {
			JSONMessage jt=jtemp;
			for(int i=1;i<strs.length;i++) {
				String s2=strs[i];
				List<Object> chr=(List<Object>)jt.get("children");
				for(Object ch : chr) {
					if(ch instanceof JSONMessage) {
						JSONMessage jm=(JSONMessage)ch;
						if(jm.get("title").equals(s2)) {
							jt=jm;
						}
					}
				}
				if(!jt.get("title").equals(s2)) {
					jt=new JSONMessage();
					jt.push("title", s2);
					jt.push("children", new ArrayList<>());
					chr.add(jt);
				}
			}
			((List<Object>)jt.get("children")).add(new HtmlBodyContent(col.toJsonString()));
			if(isLast) {
				String r=jtemp.toJSONString();
				jtemp=null;
				return r;
			}
		}else {
			String r=toTitleJson();
			if(isLast) {
				return r+","+toTitleJson(col, isLast);
			}else {
				toTitleJson(col,isLast);
				return r;
			}
		}
		return null;
	}
	private String toLastJson() {
		if(null!=jtemp) return jtemp.toJSONString();
		else return null;
	}
	public ExcelObject toExcel() throws Exception {
		this.page=null;
		JSONMessage result=getQueryResult("list,count");
		List<?> list=(List<?>) result.get("data");
		List<?> summary=(List<?>) result.get("summary");
		ExcelObject excel=new ExcelObject(this.title);
		List<SheetRow> headRow=ExcelUtil.toHeadCell(this.cols);
		List<SheetRow> bodyRow=ExcelUtil.toBodyCell(this.cols, list, summary);
		int sc=this.scol;
		int sl=this.slength;
		for(int i=0;i<this.cols.size();i++) {
			if(!this.cols.get(i).getShow()) {
				if(this.scol>i) sc--;
				else if(this.scol+this.slength>i) sl--;
			}
		}
		List<Integer[]> mergeCells=ExcelUtil.toHeadMergeCells(headRow);
		mergeCells.addAll(ExcelUtil.toBodyMergeCells(headRow.size(), bodyRow,summary.size(), sc, sl));
		List<SheetRow> rows= new ArrayList<>();
		rows.addAll(headRow);
		rows.addAll(bodyRow);
		SheetObject sheet=new SheetObject(this.title,rows.toArray(new SheetRow[] {})).setMergeCells(mergeCells);
		excel.addSheet(sheet);
		
		return excel;
	}
	@Override
	public JSONMessage toEchartsOption() throws Exception {
		this.page=null;
		JSONMessage result=getQueryResult("list");
		List<?> list=(List<?>) result.get("data");
		return EchartsUtil.toEchartsOption(this.title,this.cols,list,
			new Object[]{this.params.get("echarts_x"),this.params.get("echarts_y1"),this.params.get("echarts_y2")}
		);
	}

}
